MySQL学习笔记

数据库相关概念

数据库(Database)

保存有组织的数据的容器(通常是一个文件或一组文件)。

表(Table)

某种特定类型数据的结构化清单。

列(Column)

表中的一个字段。所有表都是由一个或多个列组成的。

行(Row)

表中的一个记录。

主键(Primary Key)

一列(或一组列),其值能够唯一区分表中每个行。

最好保证创建的每个表都具有一个主键,以便于管理和维护。

列作为主键,需要满足以下条件:

  • 任意两行都不具有相同的主键值;

  • 每个行都必须具有一个主键值(主键列不允许NULL值)。

几个主键的好习惯:

  • 不更新主键列中的值;

  • 不重用主键列的值;

  • 不再主键列中使用可能会更改的值。

获得数据库和表的信息

查看所有数据库

SHOW DATABASES;

使用数据库

USE dbname;

查看一个数据库内的表的列表

SHOW TABLES;

显示表列

SHOW COLUMNS FROM customers;

DESCRIBE customers;

要求提供一个表名。

显示服务器状态信息

SHOW STATUS;

显示授予用户的安全权限

SHOW GRANTS;

显示服务器错误或警告

SHOW ERRORS;
SHOW WARNINGS;

检索数据

检索单个列

SELECT prod_name
FROM products;

上述语句利用 SELECTproducts 表中检索一个名为 prod_name 的列,注意,这个输出是未排序的。

检索多个列

SELECT prod_id, prod_name, prod_price
FROM products;

检索所有列

SELECT *
FROM products;

注意,检索不需要的列通常会降低应用程序的性能。

检索不同的行

SELECT vend_id
FROM products;


+---------+
| vend_id |
+---------+
|    1001 |
|    1001 |
|    1001 |
|    1002 |
|    1002 |
|    1003 |
|    1003 |
|    1003 |
|    1003 |
|    1003 |
|    1003 |
|    1003 |
|    1005 |
|    1005 |
+---------+
14 rows in set (0.00 sec)

当需要检索不同值的列表时,用关键字 DISTINCT 指示MySQL,如下:

SELECT DISTINCT vend_id
FROM products;


+---------+
| vend_id |
+---------+
|    1001 |
|    1002 |
|    1003 |
|    1005 |
+---------+
4 rows in set (0.00 sec)

注意, DISTINCT 关键字应用于所有列,而不仅是前置它的列。

限制输出结果

为了返回结果中的某些行,可以使用 LIMIT 子句,如下(返回所有结果中的前五行):

SELECT prod_name
FROM products
LIMIT 5;


+--------------+
| prod_name    |
+--------------+
| .5 ton anvil |
| 1 ton anvil  |
| 2 ton anvil  |
| Detonator    |
| Bird seed    |
+--------------+
5 rows in set (0.00 sec)

如果希望返回所有结果中的从行3开始的5行,如下:

SELECT prod_name
FROM products
LIMIT 3, 5;


+--------------+
| prod_name    |
+--------------+
| Detonator    |
| Bird seed    |
| Carrots      |
| Fuses        |
| JetPack 1000 |
+--------------+
5 rows in set (0.00 sec)

注意,检索出来的第一行是行0,而不是行1。

一种替代语法,比如需要从行3开始取4行,可以使用如下语句:

SELECT prod_name
FROM products
LIMIT 4 OFFSET 3;


+-----------+
| prod_name |
+-----------+
| Detonator |
| Bird seed |
| Carrots   |
| Fuses     |
+-----------+
4 rows in set (0.00 sec)

排序检索数据

排序数据

SELECT prod_name
FROM products
ORDER BY prod_name

这条语句指示MySQL对 prod_name 列以字母顺序排序。

按多个列排序

SELECT prod_id, prod_price, prod_name
FROM products
ORDER BY prod_price, prod_name;

多关键字排序,仅在多个行具有相同的 prod_price 值时才对产品按 prod_name 排序。

指定排序方向

以价格降序排列:

SELECT prod_id, prod_price, prod_name
FROM products
ORDER BY prod_price DESC;

注意, DESC 关键字只应用到直接位于其前面的列名,如果想在多个列上进行降序排序,需要对每个列指定 DESC 关键字。

使用 ORDER BYLIMIT 的组合,可以找出最大或最小值。

SELECT prod_price
FROM products
ORDER BY prod_price DESC
LIMIT 1;

ORDER BY 子句应保证位于 FROM 子句之后,同时也应保证位于 LIMIT 之前。

过滤数据

使用WHERE子句

SELECT prod_name, prod_price
FROM products
WHERE prod_price = 2.50;


+---------------+------------+
| prod_name     | prod_price |
+---------------+------------+
| Carrots       |       2.50 |
| TNT (1 stick) |       2.50 |
+---------------+------------+
2 rows in set (0.00 sec)

这条语句从 products 表中检索两个列,只返回 prod_price 值为2.50的行。

在使用 ORDER BYWHERE 子句时,应该让 ORDER BY 位于 WHERE 之后。

操作符有 =<>!=<<=>>=BETWEEN ,只说下 BETWEEN ,如下:

SELECT prod_name, prod_price
FROM products
WHERE prod_price BETWEEN 5 AND 10;


+----------------+------------+
| prod_name      | prod_price |
+----------------+------------+
| .5 ton anvil   |       5.99 |
| 1 ton anvil    |       9.99 |
| Bird seed      |      10.00 |
| Oil can        |       8.99 |
| TNT (5 sticks) |      10.00 |
+----------------+------------+
5 rows in set (0.00 sec)

空值检查

SELECT cust_id, cust_city, cust_email
FROM customers
WHERE cust_email IS NULL;


+---------+-----------+------------+
| cust_id | cust_city | cust_email |
+---------+-----------+------------+
|   10002 | Columbus  | NULL       |
|   10005 | Chicago   | NULL       |
+---------+-----------+------------+
2 rows in set (0.00 sec)

组合WHERE子句

对于 ANDOR ,注意 AND 的优先级比 OR 高。

IN 操作符示例:

SELECT vend_id, prod_name, prod_price
FROM products
WHERE vend_id IN (1002, 1003)
ORDER BY prod_name;


+---------+----------------+------------+
| vend_id | prod_name      | prod_price |
+---------+----------------+------------+
|    1003 | Bird seed      |      10.00 |
|    1003 | Carrots        |       2.50 |
|    1003 | Detonator      |      13.00 |
|    1002 | Fuses          |       3.42 |
|    1002 | Oil can        |       8.99 |
|    1003 | Safe           |      50.00 |
|    1003 | Sling          |       4.49 |
|    1003 | TNT (1 stick)  |       2.50 |
|    1003 | TNT (5 sticks) |      10.00 |
+---------+----------------+------------+
9 rows in set (0.00 sec)

NOT 操作符示例:

SELECT vend_id, prod_name, prod_price
FROM products
WHERE vend_id NOT IN (1002, 1003)
ORDER BY prod_name;


+---------+--------------+------------+
| vend_id | prod_name    | prod_price |
+---------+--------------+------------+
|    1001 | .5 ton anvil |       5.99 |
|    1001 | 1 ton anvil  |       9.99 |
|    1001 | 2 ton anvil  |      14.99 |
|    1005 | JetPack 1000 |      35.00 |
|    1005 | JetPack 2000 |      55.00 |
+---------+--------------+------------+
5 rows in set (0.00 sec)

用通配符进行过滤

百分号( % )表示任何字符出现任意次数。

例如找出所有以词 jet 起头的产品名字:

SELECT prod_id, prod_name
FROM products
WHERE prod_name LIKE 'jet%';


+---------+--------------+
| prod_id | prod_name    |
+---------+--------------+
| JP1000  | JetPack 1000 |
| JP2000  | JetPack 2000 |
+---------+--------------+
2 rows in set (0.00 sec)

下划线( _ )表示只匹配单个任意字符。

SELECT prod_id, prod_name
FROM products
WHERE prod_name LIKE '_ ton anvil';


+---------+-------------+
| prod_id | prod_name   |
+---------+-------------+
| ANV02   | 1 ton anvil |
| ANV03   | 2 ton anvil |
+---------+-------------+
2 rows in set (0.00 sec)

用正则表达式进行过滤

基本字符匹配

SELECT prod_name
FROM products
WHERE prod_name REGEXP '1000'
ORDER BY prod_name;


+--------------+
| prod_name    |
+--------------+
| JetPack 1000 |
+--------------+
1 row in set (0.00 sec)

LIKEREGEXP 之间有一个重要的差别,就是 LIKE 会匹配整个列,如果被匹配的文本仅在列值中出现, LIKE 不会找到它,而 REGEXP 在列值内进行匹配,如果被匹配的文本在列值中出现, REGEXP 就会找到它。

默认情况下,MySQL中的正则表达式匹配不区分大小写,如果需要区分大小写,可使用 BINARY 关键字,如下:

SELECT prod_name
FROM products
WHERE prod_name REGEXP BINARY 'JetPack .000';

这里的 . 表示匹配任意一个字符。

进行OR匹配

SELECT prod_name
FROM products
WHERE prod_name REGEXP '1000|2000'
ORDER BY prod_name;


+--------------+
| prod_name    |
+--------------+
| JetPack 1000 |
| JetPack 2000 |
+--------------+
2 rows in set (0.00 sec)

匹配几个字符之一

SELECT prod_name
FROM products
WHERE prod_name REGEXP '[123] Ton'
ORDER BY prod_name;


+-------------+
| prod_name   |
+-------------+
| 1 ton anvil |
| 2 ton anvil |
+-------------+
2 rows in set (0.00 sec)

字符集合也可以否定,只需要在集合开始处放置一个 ^ 即可,比如 [^123]

可以用 - 来定义一个范围,比如 [0-9] 匹配数字0到9。

如果需要匹配特殊字符,则需要用为前导,如下:

SELECT vend_name
FROM vendors
WHERE vend_name REGEXP '.'
ORDER BY vend_name;


+--------------+
| vend_name    |
+--------------+
| Furball Inc. |
+--------------+
1 row in set (0.00 sec)

匹配字符类

预定义的字符类如下:

说明
[:alnum:] 任意字母和数字(同 [a-zA-Z0-9]
[:alpha:] 任意字符(同 [a-zA-Z]
[:blank:] 空格和制表(同 [t]
[:cntrl:] ASCII控制字符(ASCII 0到31和127)
[:digit:] 任意数字(同 [0-9]
[:graph:] 任意可打印字符,但不包括空格
[:lower:] 任意小写字母(同 [a-z]
[:print:] 任意可打印字符
[:punct:] 既不在 [:alnum:] 又不在 [:cntrl:] 中的任意字符
[:space:] 包括空格在内的任意空白字符(同 [fnrtv]
[:upper:] 任意大写字母(同 [A-Z]
[:xdigit:] 任意十六进制数字(同 [a-fA-F0-9]

匹配多个实例

元字符 说明
* 0个或多个匹配
+ 1个或多个匹配
? 0个或1个匹配
{n} 指定数目的匹配
{n,} 不少于指定数目的匹配
{n,m} 匹配数目的范围( m 不超过255)

比如下面这个例子:

SELECT prod_name
FROM products
WHERE prod_name REGEXP '([0-9] sticks?)'
ORDER BY prod_name;


+----------------+
| prod_name      |
+----------------+
| TNT (1 stick)  |
| TNT (5 sticks) |
+----------------+
2 rows in set (0.00 sec)

s 后面的 ? 使得 s 可选,因为 ? 匹配0个或1个。

下面的例子匹配连在一起的4位数字:

SELECT prod_name
FROM products
WHERE prod_name REGEXP '[[:digit:]]{4}'
ORDER BY prod_name;


+--------------+
| prod_name    |
+--------------+
| JetPack 1000 |
| JetPack 2000 |
+--------------+
2 rows in set (0.00 sec)

定位符

元字符 说明
^ 文本的开始
$ 文本的结尾
[[:<:]] 词的开始
[[:>:]] 词的结尾

下面的例子找出一个数(包括以小数点开始的数)开始的所有产品名称:

SELECT prod_name
FROM products
WHERE prod_name REGEXP '^[0-9.]'
ORDER BY prod_name;


+--------------+
| prod_name    |
+--------------+
| .5 ton anvil |
| 1 ton anvil  |
| 2 ton anvil  |
+--------------+
3 rows in set (0.00 sec)

创建计算字段

拼接字段

利用多个列的内容组合出一个列返回,使用 Concat() 函数来拼接,如下:

SELECT Concat(vend_name, '(', vend_country, ')')
FROM vendors
ORDER BY vend_name;


+-------------------------------------------+
| Concat(vend_name, '(', vend_country, ')') |
+-------------------------------------------+
| ACME(USA)                                 |
| Anvils R Us(USA)                          |
| Furball Inc.(USA)                         |
| Jet Set(England)                          |
| Jouets Et Ours(France)                    |
| LT Supplies(USA)                          |
+-------------------------------------------+
6 rows in set (0.00 sec)

至于清理多余空格,可以用 Trim()LTrim()RTrim() 来完成。

上面的例子中计算列没有名字,不能用于客户机的应用,可以用 AS 关键字赋予其别名。

SELECT Concat(RTrim(vend_name), '(', RTrim(vend_country), ')') AS vend_title
FROM vendors
ORDER BY vend_name;


+------------------------+
| vend_title             |
+------------------------+
| ACME(USA)              |
| Anvils R Us(USA)       |
| Furball Inc.(USA)      |
| Jet Set(England)       |
| Jouets Et Ours(France) |
| LT Supplies(USA)       |
+------------------------+
6 rows in set (0.00 sec)

这个 vend_title 就像实际的列一样。

执行算数运算

SELECT prod_id, quantity, item_price, quantity*item_price AS expanded_price
FROM orderitems
WHERE order_num = 20005;


+---------+----------+------------+----------------+
| prod_id | quantity | item_price | expanded_price |
+---------+----------+------------+----------------+
| ANV01   |       10 |       5.99 |          59.90 |
| ANV02   |        3 |       9.99 |          29.97 |
| TNT2    |        5 |      10.00 |          50.00 |
| FB      |        1 |      10.00 |          10.00 |
+---------+----------+------------+----------------+
4 rows in set (0.03 sec)

使用数据处理函数

文本处理函数

函数 说明
Left() 返回串左边的字符
Length() 返回串的长度
Locate() 找出串的一个子串
Lower() 将串转换为小写
LTrim() 去掉串左边的空格
Right() 返回串右边的字符
RTrim() 去掉串右边的空格
Soundex() 返回串的 SOUNDEX
SubString() 返回子串的字符
Upper() 将串转换为大写

其中, SOUNDEX 是一个能对串的发音比较而不是进行字母比较,比如下面的例子:

SELECT cust_name, cust_contact
FROM customers
WHERE Soundex(cust_contact) = Soundex('Y Lie');


+-------------+--------------+
| cust_name   | cust_contact |
+-------------+--------------+
| Coyote Inc. | Y Lee        |
+-------------+--------------+
1 row in set (0.05 sec)

其中, WHERE 子句使用 Soundex() 函数来转换 cust_contact 列值和搜索串为它们的 SOUNDEX 值,因为 Y LieY Lee 发音相似,所以它们的 SOUNDEX 值匹配。

日期和时间处理函数

常用的日期和时间处理函数

函数 说明
AddDate() 增加一个日期(天、周等)
AddTime() 增加一个时间(时、分等)
CurDate() 返回当前日期
CurTime() 返回当前时间
Date() 返回日期时间的日期部分
DateDiff() 计算两个日期之差
Date_Add() 高度灵活的日期运算函数
Date_Format() 返回一个格式化的日期或时间串
Day() 返回一个日期的天数部分
DayOfWeek() 对于一个日期,返回对应的星期几
Hour() 返回一个时间的小时部分
Minute() 返回一个时间的分钟部分
Month() 返回一个日期的月份部分
Now() 返回当前日期和时间
Second() 返回一个时间的秒部分
Time() 返回一个日期时间的时间部分
Year() 返回一个日期的年份部分

注意MySQL使用的日期格式,必须为 yyyy-mm-dd ,虽然支持两位数字的年份,但应该总是使用四位数的年份。

基本的日期示例:

SELECT cust_id, order_num
FROM orders
WHERE order_date = '2005-09-01';


+---------+-----------+
| cust_id | order_num |
+---------+-----------+
|   10001 |     20005 |
+---------+-----------+
1 row in set (0.00 sec)

但这是不可靠的,如果 order_date 值为 2005-09-01 12:00:00 ,那么 WHERE 匹配就会失败,所以应该用上面的函数来进行提取,然后再进行比较:

SELECT cust_id, order_num
FROM orders
WHERE Date(order_date) = '2005-09-01';

如果想检索出2005年9月的所有订单,可以这么写:

SELECT cust_id, order_num
FROM orders
WHERE Date(order_date) BETWEEN '2005-09-01' AND '2005-09-30';

另一种方法是这么写:

SELECT cust_id, order_num
FROM orders
WHERE Year(order_date) = 2005 AND Month(order_date) = 9;

数值处理函数

函数 说明
Abs() 返回一个数的绝对值
Cos() 返回一个角度的余弦
Exp() 返回一个数的指数值
Mod() 返回除操作的余数
Pi() 返回圆周率
Rand() 返回一个随机数
Sin() 返回一个角度的正弦
Sqrt() 返回一个数的平方根
Tan() 返回一个角度的正切

汇总数据

聚集函数

函数 说明
AVG() 返回某列的平均值
COUNT() 返回某列的行数
MAX() 返回某列的最大值
MIN() 返回某列的最小值
SUM() 返回某列值之和

AVG()函数

SELECT AVG(prod_price) AS avg_price
FROM products;


+-----------+
| avg_price |
+-----------+
| 16.133571 |
+-----------+
1 row in set (0.00 sec)

当然可以和 WHERE 组合来确定特定行的平均值。

注意: AVG() 函数忽略列值为 NULL 的行。

COUNT()函数

有两种使用方式,当使用 COUNT(*) 对表中的数目进行计数,不管表列中包含的是 NULL 还是非空值;而是用 COUNT(column) 对特定列中具有的值进行计数,忽略 NULL 值。

比如下面的例子返回 customers 表的总人数:

SELECT COUNT(*) AS num_cust
FROM customers;


+----------+
| num_cust |
+----------+
|        5 |
+----------+
1 row in set (0.00 sec)

下面的例子只对有电子邮件地址的人计数:

SELECT COUNT(cust_email) AS num_cust
FROM customers;


+----------+
| num_cust |
+----------+
|        3 |
+----------+
1 row in set (0.00 sec)

而实际上的表是这样的:

mysql> SELECT cust_id, cust_email
    -> FROM customers;
+---------+---------------------+
| cust_id | cust_email          |
+---------+---------------------+
|   10001 | ylee@coyote.com     |
|   10002 | NULL                |
|   10003 | rabbit@wascally.com |
|   10004 | sam@yosemite.com    |
|   10005 | NULL                |
+---------+---------------------+
5 rows in set (0.00 sec)

MAX()、MIN()、SUM()函数

SELECT MIN(prod_price) AS min_price
FROM products;


+-----------+
| min_price |
+-----------+
|      2.50 |
+-----------+
1 row in set (0.01 sec)

它们都会忽略列值为 NULL 的行。

聚集不同值

对于上面的 AVG()SUM()MIN()MAX()COUNT() 聚集函数,都有两个参数可用:

对所有的行执行计算,指定 ALL 参数或者不给参数(默认);
只包含不同的值,指定 DISTINCT 参数。

比如 AVG() 函数使用 DISTINCT 参数的示例:

SELECT AVG(DISTINCT prod_price) AS avg_price
FROM products
WHERE vend_id = 1003;


+-----------+
| avg_price |
+-----------+
| 15.998000 |
+-----------+
1 row in set (0.03 sec)

而不使用 DISTINCT 的时候的平均值是:

SELECT AVG(prod_price) AS avg_price
FROM products
WHERE vend_id = 1003;


+-----------+
| avg_price |
+-----------+
| 13.212857 |
+-----------+
1 row in set (0.00 sec)

说明有多个物品具有相同的较低价格,排除它们后提升了平均价格。

注意, DISTINCT 不能用于 COUNT(*)